package main

import (
	rediscache "ahutoj/web/cache/redis"
	"ahutoj/web/dao"
	mysqldao "ahutoj/web/dao/mysqlDao"
	"ahutoj/web/io/constanct"
	"ahutoj/web/io/response"
	"ahutoj/web/middlewares"
	"ahutoj/web/models"
	"ahutoj/web/routers"
	"ahutoj/web/utils"
	"context"
	"encoding/json"
	"fmt"
	"os"
	"time"
)

// @title			AHUTOJ
// @version		2.0
// @description	安工大ACM在线评测系统
// @license.name	Apache 2.0
// @contact.name	go-swagger帮助文档
// @host			oj.angriliset.top:4444
func main() {
	ConfigPath := "./config.yaml"
	if len(os.Args) >= 2 {
		ConfigPath = os.Args[1]
	}
	initAPP(ConfigPath)
	fmt.Println("error server down!")
}
func initAPP(ConfigPath string) error {
	//初始化配置文件
	err := utils.ConfigInit(ConfigPath)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	//初始化日志服务
	utils.LogInit()

	//初始化MySQL数据库
	err = mysqldao.InitMysql(nil)
	logger := utils.GetLogInstance()
	if err != nil {
		logger.Errorf("init mysql error mysqlConf:%+v, err=%s", utils.Sdump(utils.GetConfInstance().MySQLConfig), err.Error())
		os.Exit(1)
	}

	//初始化Redis数据库
	err = rediscache.InitRedisPool()
	if err != nil {
		logger.Errorf("init redis error redisConf=%+v, err=%s", utils.Sdump(utils.GetConfInstance().RedisConfig), err.Error())
		os.Exit(1)
	}
	//初始化JWT
	middlewares.InitJwt()

	//初始化websocket
	middlewares.InitWebSocket()

	middlewares.InitSnowflake(utils.GetConfInstance().StartTime, utils.GetConfInstance().MachineID)
	rbtcfg := utils.GetConfInstance().RabbitMQ
	middlewares.NewRabbitMQ(rbtcfg.Host, rbtcfg.Port, rbtcfg.Username, rbtcfg.Password, 12)
	osscfg := utils.GetConfInstance().OssConfig
	middlewares.NewOss(osscfg.Host, osscfg.Port, osscfg.AccessKeyID, osscfg.SecretAccessKey, osscfg.UseSSL)
	//初始化持久化服务
	go persistence()
	routers.InitServer()
	return nil
}
func persistence() {
	logger := utils.GetLogInstance()

	Err := middlewares.GetConsumer(constanct.JUDGECE)
	Result := middlewares.GetConsumer(constanct.JUDGERESULT)
	Info :=middlewares.GetConsumer(constanct.JUDGEINFO)

	rdfd := rediscache.GetRedis()
	defer rediscache.CloseRDB(rdfd)
	
	for {
		errs, err := Err.ConsumeMessage() //发生其他错误时，可以重新尝试获取
		if err != nil {
			logger.Errorf("call ConsumeMessage failed,queue:%v,err:%v", constanct.JUDGECE, err.Error())
			time.Sleep(20 * time.Second)
			continue
		}
		results, err := Result.ConsumeMessage()
		if err != nil {
			logger.Errorf("call ConsumeMessage failed,queue:%v,err:%v", constanct.JUDGERESULT, err.Error())
			time.Sleep(20 * time.Second) //防止日志被顶上去了~
			continue
		}
		Infos ,err:=Info.ConsumeMessage()
		if err!=nil{
			logger.Errorf("call ConsumeMessage failed,queue:%v,err:%v", constanct.JUDGEINFO, err.Error())
			time.Sleep(20 * time.Second)
			continue
		}
		//必须消费成功,否则会重复消费
		for {
			select {
			case msg := <-errs:
				if len(msg.Body) == 0{
					continue
				}
				errinfo := dao.CeInfo{}
				err := json.Unmarshal(msg.Body, &errinfo)
				if err != nil {
					logger.Errorf("call json.Unmarshal failed,err:%v", err.Error())
					time.Sleep(20 * time.Second) 
					continue
				}
				utils.TruncateString(&errinfo.Info,constanct.MaxInfoSize)
				if err := ErrinfoToDataBase(context.Background(), &errinfo); err != nil {
					logger.Errorf("call ErrinfoToDataBase failed,err:%v", err.Error())
					time.Sleep(20 * time.Second) 
					continue
				}
			case msg:= <-Infos:
				if len(msg.Body) == 0{
					continue
				}
				info := response.JudgeInfoItem{}
				err = json.Unmarshal(msg.Body, &info)
				if err != nil {
					logger.Errorf("call json.Unmarshal failed,err:%v", err.Error())
					continue
				}
				utils.TruncateString(&info.Result,constanct.MaxInfoSize)
				conn,ok:=middlewares.GetConn(info.UID)
				if !ok{
					// logger.Errorf("call GetConn failed,uid:%v", info.UID)
					continue
				}
				respJSON, err := json.Marshal(&response.WsJudgeInfoResp{
					JudgeInfoItem: &info,
					WebsocketBaseResp: response.WebsocketBaseResp{
						Response: response.CreateResponse(constanct.SuccessCode),
						Type:      constanct.JudgeInfoType,
					},
				})
				if err != nil {
					logger.Errorf("call json.Marshal failed,err:%v", err.Error())
					continue
				}
				err = conn.WriteJSON(respJSON)
				if err != nil {
					logger.Errorf("call WriteJSON failed,err:%v", err.Error())
					continue
				}
			case msg := <-results:
				submit := dao.Submit{}
				err = json.Unmarshal(msg.Body, &submit)
				if err != nil {
					logger.Errorf("call json.Unmarshal failed,err:%v", err.Error())
					Repersistence := middlewares.GetProducer()
					if err:=Repersistence.SendMessage(constanct.JUDGERESULT,submit);err!=nil{
						logger.Errorf("call Repersistence.SendMessage failed,err:%v", err.Error())
						return
					}
					continue
				}
				utils.TruncateString(&submit.Source,constanct.MaxSourceSize)
				// logger.Debugf("submit:%v", utils.Sdump(submit))
				if err := SubmitToDataBase(context.Background(), &submit); err != nil {
					logger.Errorf("call SubmitToDataBase failed,err:%v", err.Error())
					Repersistence := middlewares.GetProducer()
					if err:=Repersistence.SendMessage(constanct.JUDGERESULT,submit);err!=nil{
						logger.Errorf("call Repersistence.SendMessage failed,err:%v", err.Error())
						return
					}
					time.Sleep(20 * time.Second) 
					continue
				}
				conn,ok:=middlewares.GetConn(submit.UID)
				if !ok{
					continue
				}
				respJSON, err := json.Marshal(&response.WsJudgeResultResp{
					WebsocketBaseResp: response.WebsocketBaseResp{
						Response: response.CreateResponse(constanct.SuccessCode),
						Type:      constanct.JudgeResultType,
					},
					UID: submit.UID,
					SID : submit.SID,
					ResultACM: submit.Result,
				})
				if err != nil {
					logger.Errorf("call json.Marshal failed,err:%v", err.Error())
					continue
				}
				err = conn.WriteJSON(respJSON)
				if err != nil {
					logger.Errorf("call WriteJSON failed,err:%v", err.Error())
					continue
				}
				//---下面是通过redis---发布订阅，如果没有订阅者会进行丢弃，并且动态创建销毁频道---
				err = rediscache.Pubilsh(context.Background(), rdfd, fmt.Sprintf("%v", submit.CID), submit)
				if err != nil {
					logger.Errorf("call Pubilsh failed,err:%v", err.Error())
					continue;
				}
			}
		}
	}
}

func ErrinfoToDataBase(ctx context.Context, ceinfo *dao.CeInfo) error {
	logger := utils.GetLogInstance()
	err := mysqldao.InsertCeInfo(ctx, *ceinfo)
	// logger.Infof(utils.Sdump(ceinfo))
	if err != nil {
		logger.Errorf("call InsertCEinfo failed,ceinfo=%v, err=%v", utils.Sdump(ceinfo), err.Error())
		return err
	}
	return nil
}

func SubmitToDataBase(ctx context.Context, submit *dao.Submit) error {
	logger := utils.GetLogInstance()
	err := models.UpdateSubmit(ctx, *submit)
	if err != nil {
		logger.Errorf("call UpdateSubmit failed,submit=%v, err=%v", utils.Sdump(submit), err.Error())
		return err
	}
	err = mysqldao.IncUserSubmited(ctx, submit.UID)
	if err != nil {
		logger.Errorf("call IncUserSubmited failed, submit=%v, err=%s", submit, err.Error())
		return err
	}
	err = mysqldao.IncProblemSubmited(ctx, submit.PID)
	if err != nil {
		logger.Errorf("call IncProblemSubmited failed, submit=%v, err=%s", submit, err.Error())
		return err
	}
	// 更新题目提交代码
	if submit.CID > 0 {
		err = mysqldao.IncConProSubmit(ctx,submit.CID, submit.PID)
		if err != nil {
			logger.Errorf("call IncConProSubmit failed, submit=%v, err=%s", submit, err.Error())
			return err
		}
	}
	if submit.LID > 0 {
		err = mysqldao.IncListUserSubmit(ctx, submit.LID, submit.UID)
		if err != nil {
			logger.Errorf("call IncListUserSubmit failed, submit=%v, err=%s", submit, err.Error())
			return err
		}
	}
	if submit.Result == constanct.OJ_AC {
		err := mysqldao.IncUserSolved(ctx, submit.UID)
		if err != nil {
			logger.Errorf("call IncUserSolved failed,submit=%v, err=%v", utils.Sdump(submit), err.Error())
			return err
		}
		err = mysqldao.IncProblemSolved(ctx, submit.PID)
		if err != nil {
			logger.Errorf("call IncProblemSolved failed,submit=%v, err=%v", utils.Sdump(submit), err.Error())
			return err
		}
		if submit.CID > 0 {
			err = mysqldao.IncConProSolved(ctx, submit.CID, submit.PID)
			if err != nil {
				logger.Errorf("call IncConProSolved failed,submit=%v, err=%v", utils.Sdump(submit), err.Error())
				return err
			}
		}
		// if submit.LID > 0 {
		// 	err = mysqldao.IncListSolved(ctx, submit.LID, submit.PID)
		// 	if err != nil {
		// 		logger.Errorf("call IncLabProSolved failed,submit=%v, err=%v", utils.Sdump(submit), err.Error())
		// 		return err
		// 	}
		// }
	}
	return err
}
